package org.hzero.interfaces.common.handler;


import org.hzero.interfaces.common.constants.KnifeInterfaceCodeEnum;
import org.hzero.interfaces.common.constants.KnifeInterfaceLogDirection;
import org.hzero.interfaces.common.entity.KnifeInterfaceLogRecord;
import org.hzero.interfaces.common.helper.director.KnifeInterfaceLogDirector;
import org.hzero.interfaces.common.helper.impl.KnifeRestTemplateInterfaceLogHelperImpl;
import org.hzero.interfaces.common.utils.KnifeLogParamUtil;
import org.hzero.interfaces.common.utils.KnifeReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * cglib代理实际处理的类
 * @author: tianhao.luo@hand-china.com 2021/7/1  上午11:14
 */
public class KnifeCglibHandler implements MethodInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(KnifeCglibHandler.class);

    private final RestTemplate restTemplate;
    /**
     * 如果不传入methodNames,则不会进行监控
     */
    private Set<String> methodNameSet;
    /**
     * 接口编码及描述
     */
    private final KnifeInterfaceCodeEnum knifeInterfaceCodeEnum;

    public KnifeCglibHandler(RestTemplate restTemplate, KnifeInterfaceCodeEnum knifeInterfaceCodeEnum, String... methodNames) {
        this.restTemplate = restTemplate;
        this.knifeInterfaceCodeEnum = knifeInterfaceCodeEnum;
        if (methodNames != null && methodNames.length != 0) {
            this.methodNameSet = new HashSet<>(methodNames.length);
            methodNameSet.addAll(Arrays.asList(methodNames));
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String name = methodProxy.getSignature().getName();
        if (methodNameSet != null && methodNameSet.contains(name)) {
            Object[] args = KnifeLogParamUtil.deepCloneArgs(objects, ServletRequest.class, BindingResult.class);
            // 记录请求前时间
            Date startDate = new Date();
            // 请求
            Object invoke = null;
            KnifeInterfaceLogRecord knifeInterfaceLogRecord = new KnifeInterfaceLogRecord(startDate, KnifeInterfaceLogDirection.OUT, knifeInterfaceCodeEnum);
            KnifeRestTemplateInterfaceLogHelperImpl defaultHitfInterfaceLogHelper;
            KnifeInterfaceLogDirector knifeInterfaceLogDirector;
            try {
                invoke = methodProxy.invoke(restTemplate, objects);
                defaultHitfInterfaceLogHelper = new KnifeRestTemplateInterfaceLogHelperImpl(knifeInterfaceLogRecord);
                knifeInterfaceLogDirector = new KnifeInterfaceLogDirector(defaultHitfInterfaceLogHelper);
                knifeInterfaceLogDirector.createHitfInterfaceLog(args, (ResponseEntity<?>) invoke, method);
            } catch (Throwable throwable) {
                String errorMsg = "";
                if (throwable instanceof InvocationTargetException){
                    InvocationTargetException invocationTargetException = (InvocationTargetException) throwable;
                    Throwable targetException = invocationTargetException.getTargetException();
                    Class<? extends Throwable> aClass = targetException.getClass();
                    Method getResponseBodyAsString = KnifeReflectUtil.getMethodNotRecursion(aClass, "getResponseBodyAsString");
                    if(getResponseBodyAsString != null){
                        if (!getResponseBodyAsString.isAccessible()) getResponseBodyAsString.setAccessible(true);
                        errorMsg = (String) getResponseBodyAsString.invoke(targetException);
                        throwable = new Exception(errorMsg);
                    }
                }
                // 构造错误日志
                defaultHitfInterfaceLogHelper = new KnifeRestTemplateInterfaceLogHelperImpl(knifeInterfaceLogRecord,new Exception(throwable));
                knifeInterfaceLogDirector = new KnifeInterfaceLogDirector(defaultHitfInterfaceLogHelper);
                knifeInterfaceLogDirector.createHitfInterfaceLog(args, (ResponseEntity<?>) invoke, method);
                throw new RuntimeException(errorMsg);
            }
            return invoke;
        } else {
            return methodProxy.invoke(restTemplate, objects);
        }
    }
}
